//
//  AliyunOSSManager.m
//  OSSDemo
//
//  Created by iOS on 2017/5/26.
//  Copyright © 2017年 MS. All rights reserved.
//

#import "AliyunOSSManager.h"
#import <AliyunOSSiOS/AliyunOSSiOS.h>

NSString *const AccessKey = @"LTAIW0LbHihMqJiX";
NSString *const SecretKey = @"c5VswKAZMmjd4RnNsX4Gr0hV4n06ML";
NSString *const endPoint = @"oss-cn-zhangjiakou.aliyuncs.com";//
NSString *const multipartUploadKey = @"multipartUploadObject";

OSSClient * client;
static AliyunOSSManager *manager;

@implementation AliyunOSSManager

+ (instancetype)shareManager
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[self alloc] init];
    });
    
    return manager;
}

- (void)setupEnvironment
{
    // 打开调试log
//    [OSSLog enableLog];
    
    // 在本地生成一些文件来演示
//    [self initLocalFile];
    
    // 初始化SDK
    [self initOSSClient];
}

- (void)runDemo
{
    
}

- (NSString *)getDocumentDirectory
{
    NSString *path = NSHomeDirectory();
    NSLog(@"NSHomeDirectory:%@",path);
    NSString *userName = NSUserName();
    NSString *rootPath = NSHomeDirectoryForUser(userName);
    NSLog(@"NSHomeDirectoryForUser:%@",rootPath);
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString * documentsDirectory = [paths objectAtIndex:0];
    return documentsDirectory;
}

- (void)initLocalFile
{
    NSFileManager *fm = [NSFileManager defaultManager];
    NSString *mainDir = [self getDocumentDirectory];
    
    NSArray *fileNameArray = @[@"file1k", @"file10k", @"file100k", @"file1m", @"file10m", @"fileDirA/", @"fileDirB/"];
    NSArray *fileSizeArray = @[@1024, @10240, @102400, @1024000, @10240000, @1024, @1024];
    
    NSMutableData *basePart = [NSMutableData dataWithCapacity:1024];
    for (int i = 0; i < 1024/4; i++)
    {
        u_int32_t randomBit = arc4random();
        [basePart appendBytes:(void*)&randomBit length:4];
    }
    
    for (int i = 0; i < [fileNameArray count]; i++)
    {
        NSString *name = [fileNameArray objectAtIndex:i];
        long size = [[fileSizeArray objectAtIndex:i] longValue];
        NSString *newFilePath = [mainDir stringByAppendingPathComponent:name];
        if ([fm fileExistsAtPath:newFilePath]) {
            [fm removeItemAtPath:newFilePath error:nil];
        }
        [fm createFileAtPath:newFilePath contents:nil attributes:nil];
        NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:newFilePath];
        for (int k = 0; k < size/1024; k++) {
            [f writeData:basePart];
        }
        [f closeFile];
    }
    
    NSLog(@"main bundle: %@", mainDir);
}

- (void)initOSSClient
{
    // 明文设置AccessKeyId/AccessKeySecret的方式建议只在测试时使用
//    id<OSSCredentialProvider> credential = [[OSSPlainTextAKSKPairCredentialProvider alloc] initWithPlainTextAccessKey:AccessKey secretKey:SecretKey];

    // STS鉴权模式
    id<OSSCredentialProvider> credential2 = [[OSSFederationCredentialProvider alloc] initWithFederationTokenGetter:^OSSFederationToken * {
        // 构造请求访问您的业务server
        NSURL * url = [NSURL URLWithString:@"http://takeout.minweixian.com/index.php?ctrl=api_common&func=ossSts"];
        NSURLRequest * request = [NSURLRequest requestWithURL:url];
        OSSTaskCompletionSource * tcs = [OSSTaskCompletionSource taskCompletionSource];
        NSURLSession * session = [NSURLSession sharedSession];
        // 发送请求
        NSURLSessionTask * sessionTask = [session dataTaskWithRequest:request
                                                    completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                        if (error) {
                                                            [tcs setError:error];
                                                            return;
                                                        }
                                                        [tcs setResult:data];
                                                    }];
        [sessionTask resume];
        // 需要阻塞等待请求返回
        [tcs.task waitUntilFinished];
        // 解析结果
        if (tcs.task.error) {
            NSLog(@"get token error: %@", tcs.task.error);
            return nil;
        } else {
            // 返回数据是json格式，需要解析得到token的各个字段
            NSDictionary * object = [NSJSONSerialization JSONObjectWithData:tcs.task.result
                                                                    options:kNilOptions
                                                                      error:nil];
            OSSFederationToken * token = [OSSFederationToken new];
            token.tAccessKey = [object objectForKey:@"AccessKeyId"];
            token.tSecretKey = [object objectForKey:@"AccessKeySecret"];
            token.tToken = [object objectForKey:@"SecurityToken"];
            token.expirationTimeInGMTFormat = [object objectForKey:@"Expiration"];
            NSLog(@"get token: %@", token);
            return token;
        }
    }];
    
    client = [[OSSClient alloc] initWithEndpoint:endPoint credentialProvider:credential2];
}

- (void)createBucket
{
    OSSCreateBucketRequest *create = [OSSCreateBucketRequest new];
    create.bucketName = @"ios_bucket";
    create.xOssACL = @"public-read-write";
    create.location = @"oss-cn-zhangjiakou-internal";
    
    OSSTask *createTask = [client createBucket:create];
    
    [createTask continueWithBlock:^id(OSSTask *task) {
        if (!task.error) {
            NSLog(@"create bucket success!");
        } else {
            NSLog(@"create bucket failed, error: %@", task.error);
        }
        return nil;
    }];
}

- (void)deleteBucket
{
    OSSDeleteBucketRequest *delete = [OSSDeleteBucketRequest new];
    delete.bucketName = @"<bucketName>";
    
    OSSTask *deleteTask = [client deleteBucket:delete];
    
    [deleteTask continueWithBlock:^id(OSSTask *task) {
        if (!task.error)
        {
            NSLog(@"delete bucket success!");
        }
        else
        {
            NSLog(@"delete bucket failed, error: %@", task.error);
        }
        return nil;
    }];
}

- (void)listObjectsInBucket
{
    OSSGetBucketRequest *getBucket = [OSSGetBucketRequest new];
    getBucket.bucketName = @"android-test";
    getBucket.delimiter = @"";
    getBucket.prefix = @"";
    
    OSSTask *getBucketTask = [client getBucket:getBucket];
    
    [getBucketTask continueWithBlock:^id(OSSTask *task) {
        if (!task.error)
        {
            OSSGetBucketResult * result = task.result;
            NSLog(@"get bucket success!");
            for (NSDictionary * objectInfo in result.contents)
            {
                NSLog(@"list object: %@", objectInfo);
            }
        }
        else
        {
            NSLog(@"get bucket failed, error: %@", task.error);
        }
        
        return nil;
    }];
}


- (void)uploadObjectAsync
{
    OSSPutObjectRequest *put = [OSSPutObjectRequest new];
    
    // required fields
    put.bucketName = @"mwx-img";
    put.objectKey = @"test4";
    NSString * docDir = [[NSBundle mainBundle] pathForResource:@"WechatIMG12" ofType:@"png"];
//    put.uploadingFileURL = [NSURL fileURLWithPath:docDir];
    UIImage *img = [UIImage imageWithContentsOfFile:docDir];
    /**
     图片转data
     */
//    NSData *imgData = UIImagePNGRepresentation(img);
    NSData *imgData = UIImageJPEGRepresentation(img, 0.3);
    put.uploadingData = imgData;
    
    // optional fields
    put.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
        NSLog(@"%lld, %lld, %lld", bytesSent, totalByteSent, totalBytesExpectedToSend);
    };
    put.contentType = @"png";
    put.contentMd5 = @"";
    put.contentEncoding = @"";
    put.contentDisposition = @"";
    
    OSSTask *putTask = [client putObject:put];
    
    [putTask continueWithBlock:^id(OSSTask *task) {
        NSLog(@"objectKey: %@", put.objectKey);
        if (!task.error)
        {
            NSLog(@"upload object success!");
        }
        else
        {
            NSLog(@"upload object failed, error: %@" , task.error);
        }
        
        return nil;
    }];
}

// 同步上传
- (void)uploadObjectSync
{
    OSSPutObjectRequest *put = [OSSPutObjectRequest new];
    
    // required fields
    put.bucketName = @"android-test";
    put.objectKey = @"file1m";
    NSString *docDir = [self getDocumentDirectory];
    put.uploadingFileURL = [NSURL fileURLWithPath:[docDir stringByAppendingPathComponent:@"file1m"]];
    
    // optional fields
    put.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
        NSLog(@"%lld, %lld, %lld", bytesSent, totalByteSent, totalBytesExpectedToSend);
    };
    put.contentType = @"";
    put.contentMd5 = @"";
    put.contentEncoding = @"";
    put.contentDisposition = @"";
    
    OSSTask *putTask = [client putObject:put];
    
    [putTask waitUntilFinished]; // 阻塞直到上传完成
    
    if (!putTask.error)
    {
        NSLog(@"upload object success!");
    }
    else
    {
        NSLog(@"upload object failed, error: %@" , putTask.error);
    }
}

// 追加上传
- (void)appendObject
{
    OSSAppendObjectRequest *append = [OSSAppendObjectRequest new];
    
    // 必填字段
    append.bucketName = @"android-test";
    append.objectKey = @"file1m";
    append.appendPosition = 0; // 指定从何处进行追加
    NSString *docDir = [self getDocumentDirectory];
    append.uploadingFileURL = [NSURL fileURLWithPath:[docDir stringByAppendingPathComponent:@"file1m"]];
    
    // 可选字段
    append.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
        NSLog(@"%lld, %lld, %lld", bytesSent, totalByteSent, totalBytesExpectedToSend);
    };
    // append.contentType = @"";
    // append.contentMd5 = @"";
    // append.contentEncoding = @"";
    // append.contentDisposition = @"";
    
    OSSTask *appendTask = [client appendObject:append];
    
    [appendTask continueWithBlock:^id(OSSTask *task) {
        NSLog(@"objectKey: %@", append.objectKey);
        if (!task.error)
        {
            NSLog(@"append object success!");
            OSSAppendObjectResult * result = task.result;
            NSString * etag = result.eTag;
            long nextPosition = result.xOssNextAppendPosition;
            NSLog(@"etag: %@, nextPosition: %ld", etag, nextPosition);
        }
        else
        {
            NSLog(@"append object failed, error: %@" , task.error);
        }
        
        return nil;
    }];
}

- (void)downloadObjectAsync
{
    OSSGetObjectRequest *request = [OSSGetObjectRequest new];
    // required
    request.bucketName = @"android-test";
    request.objectKey = @"file1m";
    
    //optional
    request.downloadProgress = ^(int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
        NSLog(@"%lld, %lld, %lld", bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
    };
    // NSString * docDir = [self getDocumentDirectory];
    // request.downloadToFileURL = [NSURL fileURLWithPath:[docDir stringByAppendingPathComponent:@"downloadfile"]];
    
    OSSTask *getTask = [client getObject:request];
    
    [getTask continueWithBlock:^id(OSSTask *task) {
        if (!task.error)
        {
            NSLog(@"download object success!");
            OSSGetObjectResult * getResult = task.result;
            NSLog(@"download dota length: %lu", [getResult.downloadedData length]);
        }
        else
        {
            NSLog(@"download object failed, error: %@" ,task.error);
        }
        
        return nil;
    }];
}

// 同步下载
- (void)downloadObjectSync
{
    OSSGetObjectRequest *request = [OSSGetObjectRequest new];
    // required
    request.bucketName = @"android-test";
    request.objectKey = @"file1m";
    
    //optional
    request.downloadProgress = ^(int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
        NSLog(@"%lld, %lld, %lld", bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
    };
    // NSString * docDir = [self getDocumentDirectory];
    // request.downloadToFileURL = [NSURL fileURLWithPath:[docDir stringByAppendingPathComponent:@"downloadfile"]];
    
    OSSTask *getTask = [client getObject:request];
    
    [getTask waitUntilFinished];
    
    if (!getTask.error)
    {
        OSSGetObjectResult * result = getTask.result;
        NSLog(@"download data length: %lu", [result.downloadedData length]);
    }
    else
    {
        NSLog(@"download data error: %@", getTask.error);
    }
}

// 获取meta
- (void)headObject
{
    OSSHeadObjectRequest *head = [OSSHeadObjectRequest new];
    head.bucketName = @"android-test";
    head.objectKey = @"file1m";
    
    OSSTask *headTask = [client headObject:head];
    
    [headTask continueWithBlock:^id(OSSTask *task) {
        if (!task.error)
        {
            OSSHeadObjectResult * headResult = task.result;
            NSLog(@"all response header: %@", headResult.httpResponseHeaderFields);
            
            // some object properties include the 'x-oss-meta-*'s
            NSLog(@"head object result: %@", headResult.objectMeta);
        }
        else
        {
            NSLog(@"head object error: %@", task.error);
        }
        return nil;
    }];
}

// 删除Object
- (void)deleteObject
{
    OSSDeleteObjectRequest *delete = [OSSDeleteObjectRequest new];
    delete.bucketName = @"android-test";
    delete.objectKey = @"file1m";
    
    OSSTask *deleteTask = [client deleteObject:delete];
    
    [deleteTask continueWithBlock:^id(OSSTask *task) {
        if (!task.error)
        {
            NSLog(@"delete success !");
        }
        else
        {
            NSLog(@"delete erorr, error: %@", task.error);
        }
        return nil;
    }];
}

// 复制Object
- (void)copyObjectAsync
{
    OSSCopyObjectRequest *copy = [OSSCopyObjectRequest new];
    copy.bucketName = @"ios-test"; // 复制到哪个bucket
    copy.objectKey = @"file_copy_to"; // 复制为哪个object
    copy.sourceCopyFrom = [NSString stringWithFormat:@"/%@/%@", @"ios-test", @"file1m"]; // 从哪里复制
    
    OSSTask *copyTask = [client copyObject:copy];
    
    [copyTask continueWithBlock:^id(OSSTask *task) {
        if (!task.error)
        {
            NSLog(@"copy success!");
        }
        else
        {
            NSLog(@"copy error, error: %@", task.error);
        }
        
        return nil;
    }];
}

// 签名URL授予第三方访问
- (void)signAccessObjectURL
{
    NSString *constrainURL = nil;
    NSString *publicURL = nil;
    
    // sign constrain url
    OSSTask *task = [client presignConstrainURLWithBucketName:@"<bucket name>"
                                                 withObjectKey:@"<object key>"
                                        withExpirationInterval:60 * 30];
    if (!task.error)
    {
        constrainURL = task.result;
    }
    else
    {
        NSLog(@"error: %@", task.error);
    }
    
    // sign public url
    task = [client presignPublicURLWithBucketName:@"<bucket name>"
                                    withObjectKey:@"<object key>"];
    if (!task.error)
    {
        publicURL = task.result;
    }
    else
    {
        NSLog(@"sign url error: %@", task.error);
    }
}

// 分块上传
- (void)multipartUpload
{
    
    __block NSString *uploadId = nil;
    __block NSMutableArray *partInfos = [NSMutableArray new];
    
    NSString *uploadToBucket = @"android-test";
    NSString *uploadObjectkey = @"file20m";
    
    OSSInitMultipartUploadRequest *init = [OSSInitMultipartUploadRequest new];
    init.bucketName = uploadToBucket;
    init.objectKey = uploadObjectkey;
    init.contentType = @"application/octet-stream";
    init.objectMeta = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"value1", @"x-oss-meta-name1", nil];
    
    OSSTask *initTask = [client multipartUploadInit:init];
    
    [initTask waitUntilFinished];
    
    if (!initTask.error)
    {
        OSSInitMultipartUploadResult *result = initTask.result;
        uploadId = result.uploadId;
        NSLog(@"init multipart upload success: %@", result.uploadId);
    }
    else
    {
        NSLog(@"multipart upload failed, error: %@", initTask.error);
        return;
    }
    
    for (int i = 1; i <= 20; i++)
    {
        @autoreleasepool {
            OSSUploadPartRequest *uploadPart = [OSSUploadPartRequest new];
            uploadPart.bucketName = uploadToBucket;
            uploadPart.objectkey = uploadObjectkey;
            uploadPart.uploadId = uploadId;
            uploadPart.partNumber = i; // part number start from 1
            
            NSString * docDir = [self getDocumentDirectory];
            // uploadPart.uploadPartFileURL = [NSURL URLWithString:[docDir stringByAppendingPathComponent:@"file1m"]];
            uploadPart.uploadPartData = [NSData dataWithContentsOfFile:[docDir stringByAppendingPathComponent:@"file1m"]];
            
            OSSTask *uploadPartTask = [client uploadPart:uploadPart];
            
            [uploadPartTask waitUntilFinished];
            
            if (!uploadPartTask.error)
            {
                OSSUploadPartResult * result = uploadPartTask.result;
                uint64_t fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:uploadPart.uploadPartFileURL.absoluteString error:nil] fileSize];
                [partInfos addObject:[OSSPartInfo partInfoWithPartNum:i eTag:result.eTag size:fileSize]];
            }
            else
            {
                NSLog(@"upload part error: %@", uploadPartTask.error);
                return;
            }
        }
    }
    
    OSSCompleteMultipartUploadRequest *complete = [OSSCompleteMultipartUploadRequest new];
    complete.bucketName = uploadToBucket;
    complete.objectKey = uploadObjectkey;
    complete.uploadId = uploadId;
    complete.partInfos = partInfos;
    
    OSSTask *completeTask = [client completeMultipartUpload:complete];
    
    [completeTask waitUntilFinished];
    
    if (!completeTask.error)
    {
        NSLog(@"multipart upload success!");
    }
    else
    {
        NSLog(@"multipart upload failed, error: %@", completeTask.error);
        return;
    }
}

// 罗列分块
- (void)listParts
{
    OSSListPartsRequest *listParts = [OSSListPartsRequest new];
    listParts.bucketName = @"android-test";
    listParts.objectKey = @"file3m";
    listParts.uploadId = @"265B84D863B64C80BA552959B8B207F0";
    
    OSSTask *listPartTask = [client listParts:listParts];
    
    [listPartTask continueWithBlock:^id(OSSTask *task) {
        if (!task.error)
        {
            NSLog(@"list part result success!");
            OSSListPartsResult *listPartResult = task.result;
            for (NSDictionary *partInfo in listPartResult.parts)
            {
                NSLog(@"each part: %@", partInfo);
            }
        }
        else
        {
            NSLog(@"list part result error: %@", task.error);
        }
        
        return nil;
    }];
}

// 断点续传
- (void)resumableUpload
{
    __block NSString *recordKey;
    
    NSString *docDir = [self getDocumentDirectory];
    NSString *filePath = [docDir stringByAppendingPathComponent:@"file10m"];
    NSString *bucketName = @"android-test";
    NSString *objectKey = @"uploadKey";
    
    [[[[[[OSSTask taskWithResult:nil] continueWithBlock:^id(OSSTask *task) {
        // 为该文件构造一个唯一的记录键
        NSURL *fileURL = [NSURL fileURLWithPath:filePath];
        NSDate *lastModified;
        NSError *error;
        
        [fileURL getResourceValue:&lastModified forKey:NSURLContentModificationDateKey error:&error];
        
        if (error)
        {
            return [OSSTask taskWithError:error];
        }
        
        recordKey = [NSString stringWithFormat:@"%@-%@-%@-%@", bucketName, objectKey, [OSSUtil getRelativePath:filePath], lastModified];
        // 通过记录键查看本地是否保存有未完成的UploadId
        NSUserDefaults * userDefault = [NSUserDefaults standardUserDefaults];
        return [OSSTask taskWithResult:[userDefault objectForKey:recordKey]];
    }] continueWithSuccessBlock:^id(OSSTask *task) {
        if (!task.result) {
            // 如果本地尚无记录，调用初始化UploadId接口获取
            OSSInitMultipartUploadRequest * initMultipart = [OSSInitMultipartUploadRequest new];
            initMultipart.bucketName = bucketName;
            initMultipart.objectKey = objectKey;
            initMultipart.contentType = @"application/octet-stream";
            
            return [client multipartUploadInit:initMultipart];
        }
        
        OSSLogVerbose(@"An resumable task for uploadid: %@", task.result);
        
        return task;
    }] continueWithSuccessBlock:^id(OSSTask *task) {
        NSString * uploadId = nil;
        
        if (task.error) {
            return task;
        }
        
        if ([task.result isKindOfClass:[OSSInitMultipartUploadResult class]])
        {
            uploadId = ((OSSInitMultipartUploadResult *)task.result).uploadId;
        }
        else
        {
            uploadId = task.result;
        }
        
        if (!uploadId)
        {
            return [OSSTask taskWithError:[NSError errorWithDomain:OSSClientErrorDomain
                                                              code:OSSClientErrorCodeNilUploadid
                                                          userInfo:@{OSSErrorMessageTOKEN: @"Can't get an upload id"}]];
        }
        // 将“记录键：UploadId”持久化到本地存储
        NSUserDefaults * userDefault = [NSUserDefaults standardUserDefaults];
        [userDefault setObject:uploadId forKey:recordKey];
        [userDefault synchronize];
        
        return [OSSTask taskWithResult:uploadId];
    }] continueWithSuccessBlock:^id(OSSTask *task) {
        // 持有UploadId上传文件
        OSSResumableUploadRequest * resumableUpload = [OSSResumableUploadRequest new];
        resumableUpload.bucketName = bucketName;
        resumableUpload.objectKey = objectKey;
        resumableUpload.uploadId = task.result;
        resumableUpload.uploadingFileURL = [NSURL fileURLWithPath:filePath];
        resumableUpload.uploadProgress = ^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
            NSLog(@"%lld %lld %lld", bytesSent, totalBytesSent, totalBytesExpectedToSend);
        };
        return [client resumableUpload:resumableUpload];
    }] continueWithBlock:^id(OSSTask *task) {
        if (task.error)
        {
            if ([task.error.domain isEqualToString:OSSClientErrorDomain] && task.error.code == OSSClientErrorCodeCannotResumeUpload)
            {
                // 如果续传失败且无法恢复，需要删除本地记录的UploadId，然后重启任务
                [[NSUserDefaults standardUserDefaults] removeObjectForKey:recordKey];
            }
        }
        else
        {
            NSLog(@"upload completed!");
            // 上传成功，删除本地保存的UploadId
            [[NSUserDefaults standardUserDefaults] removeObjectForKey:recordKey];
        }
        
        return nil;
    }];
}

-(UIImage *)imageCompressForWidth:(UIImage *)sourceImage targetWidth:(CGFloat)defineWidth
{
    CGSize imageSize = sourceImage.size;
    CGFloat width = imageSize.width;
    CGFloat height = imageSize.height;
    CGFloat targetWidth = defineWidth;
    CGFloat targetHeight = (targetWidth / width) * height;
    UIGraphicsBeginImageContext(CGSizeMake(targetWidth, targetHeight));
    [sourceImage drawInRect:CGRectMake(0,0,targetWidth, targetHeight)];
    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

@end
